#include "Node.h"

namespace DreamEngine
{

CNode::CNode(void):
	m_pParent(MATH_NULL),

	m_position(0.0f, 0.0f, 0.0f),
	m_derivedPos(0.0f, 0.0f, 0.0f),
	m_derivedScale(1.0f, 1.0f, 1.0f),
	m_scale(1.0f, 1.0f, 1.0f)
{

}

CNode::CNode(const CString& name, CNode* pParent/* =MATH_NULL */):
	m_name(name),
	m_position(0.0f, 0.0f, 0.0f),
	m_derivedPos(0.0f, 0.0f, 0.0f),
	m_derivedScale(1.0f, 1.0f, 1.0f),
	m_scale(1.0f, 1.0f, 1.0f)
{
	Attach(pParent);
}

CNode::~CNode(void)
{
}

void CNode::SetName(const CString& name)
{
	m_name = name;
}

CNode* CNode::GetChild(const CString& name)const
{
	for (int i=0; i<int(m_childs.size()); ++i)
	{
		if (m_childs[i]->GetName() == name)
		{
			return m_childs[i];
		}
	}

	return MATH_NULL;
}

CNode* CNode::GetChild(int index)const
{
	if (index<int(m_childs.size()) && index>=0)
	{
		return m_childs[index];
	}

	return MATH_NULL;
}

void CNode::Attach(CNode* pParent, bool isFlashMat/*=true*/)
{
	if (this==pParent) return;

	m_pParent = pParent;

	if (isFlashMat && m_pParent!=MATH_NULL)
	{
		_UpdateDerived();
		_UpdateChilds(this);
	}
}

void CNode::DeAttach(bool isFlashMat/*=true*/)
{
	if (isFlashMat && m_pParent!=MATH_NULL)
	{
		m_pParent = MATH_NULL;
		_UpdateDerived();
		_UpdateChilds(this);
	}
}

void CNode::AddChild(CNode* pChild, bool isFlashMat/*=true*/)
{
	if (this == pChild) return;

	m_childs.push_back(pChild);
	pChild->Attach(this, isFlashMat);
	
}

void CNode::RemoveAllChild(bool isFlashMat/* =true */)
{
	for (int i=0; i<int(m_childs.size()); ++i)
	{
		m_childs[i]->DeAttach(isFlashMat);
	}
	m_childs.clear();
}

void CNode::SetScale(const CVector3& sacle)
{
	m_derivedScale = m_scale = sacle;
	if (m_pParent != MATH_NULL)
	{
		const CVector3& parentScale = m_pParent->GetDerivedScale();
		m_derivedScale._x *= parentScale._x;
		m_derivedScale._y *= parentScale._y;
		m_derivedScale._z *= parentScale._z;
	}
	_UpdateDerived();
	_UpdateChilds(this);

	CMatrix16::MakeTranslate(&m_worldMat, m_position, m_scale, m_quaternion);
}

void CNode::SetScale(float sx, float sy, float sz)
{
	SetScale(CVector3(sx, sy, sz));
}

void CNode::SetPosition(const CVector3& pos)
{
	m_derivedPos = m_position = pos;

	if (m_pParent != MATH_NULL)
		m_derivedPos+=m_position;
	
	_UpdateDerived();
	_UpdateChilds(this);

	CMatrix16::MakeTranslate(&m_worldMat, m_position, m_scale, m_quaternion);
}

void CNode::SetPosition(float x, float y, float z)
{
	SetPosition(CVector3(x,y,z));
}

void CNode::SetQuaternion(const CQuaternion& qua)
{
	m_deriveQua = m_quaternion = qua;

	if (m_pParent != MATH_NULL)
		m_deriveQua *= m_pParent->GetDerivedQuaternion();
	
	_UpdateDerived();
	_UpdateChilds(this);

	CMatrix16::MakeTranslate(&m_worldMat, m_position, m_scale, m_quaternion);
}

void CNode::SetQuaternion(float w, float x, float y, float z)
{
	SetQuaternion(CQuaternion(w, x, y, z));
}

void CNode::Yaw(float angle)
{
	CQuaternion qua;
	qua.Yaw(angle);
	
	SetQuaternion(qua);
}

void CNode::Roll(float angle)
{
	CQuaternion qua;
	qua.Roll(angle);

	SetQuaternion(qua);
}

void CNode::Pitch(float angle)
{
	CQuaternion qua;
	qua.Pitch(angle);

	SetQuaternion(qua);
}

void CNode::Rotate(float angle, const CVector3& axis/* =CVector3::NEGATIVE_UNIT_Y */)
{
	CQuaternion qua;
	qua.Rotate(axis, angle);

	SetQuaternion(qua);
}

void CNode::RotateYawPitchRoll(float yaw, float pitch, float roll)
{
	CQuaternion qua;
	qua.RotateYawPitchRoll(yaw, pitch, roll);

	SetQuaternion(qua);
}

void CNode::_UpdateChilds(CNode* pParent)
{
	if (pParent==MATH_NULL) return;

	NodeLst::iterator iter;
	CNode::NodeLst& childs = pParent->m_childs;

	for (iter=childs.begin(); iter!=childs.end(); ++iter)
	{
		(*iter)->_UpdateDerived();

		if ((*iter)->HasChild())
			_UpdateChilds((*iter));
	}
}

void CNode::_UpdateDerived()
{
	m_derivedPos	= m_position;
	m_derivedScale	= m_scale;
	m_deriveQua		= m_quaternion;

	if (m_pParent != MATH_NULL)
	{
		m_derivedPos	+= m_pParent->GetDerivedPosition();
		
		const CVector3& derivedScale = m_pParent->GetDerivedScale();
		m_derivedScale._x *= derivedScale._x;
		m_derivedScale._y *= derivedScale._y;
		m_derivedScale._z *= derivedScale._z;

		m_deriveQua *= m_pParent->GetDerivedQuaternion();
	}

	CMatrix16::MakeTranslate(&m_derivedWorldMat, m_derivedPos, m_derivedScale, m_deriveQua);
}

}

